const {createServer} = require("http");

const methods = Object.create(null);

createServer((request, response) => {
  // 有哪些方法
  let handler = methods[request.method] || notAllowed;
  // 去处理这些请求 
  handler(request)
    .catch(error => {
      // 有错误 就返回
      if (error.status != null) return error;
      // 不然返回个默认的 错误
      return {body: String(error), status: 500};
    })
    .then(({body, status = 200, type = "text/plain"}) => {
      // 拿到数据 是 text的 
      // 写个头部
       response.writeHead(status, {"Content-Type": type});
      //  返回的数据要在pipe 管道里面 排队 
       if (body && body.pipe) body.pipe(response);
       else response.end(body);
      //  没有pipe的话 直接发出去
    });
}).listen(8000);
// 监听了 8000 端口

// 这个方法 不能用
async function notAllowed(request) {
  return {
    status: 405,
    body: `Method ${request.method} not allowed.`
  };
}

var {parse} = require("url");
var {resolve, sep} = require("path");

var baseDirectory = process.cwd();

function urlPath(url) {
  let {pathname} = parse(url);
  // 解析url 
  // 拿到第一个
  let path = resolve(decodeURIComponent(pathname).slice(1));
  if (path != baseDirectory &&
      !path.startsWith(baseDirectory + sep)) {
        // 不是这个目录
    throw {status: 403, body: "Forbidden"};
  }
  return path;
}

const {createReadStream} = require("fs");
const {stat, readdir} = require("fs").promises;
const mime = require("mime");

methods.GET = async function(request) {
  let path = urlPath(request.url);
  // 地址
  let stats;
  try {
    // 文件状态
    stats = await stat(path);
  } catch (error) {
    // 当文件不存在时，`stat`会抛出一个错误对象，`code`属性为`'ENOENT'`。
    //  这些有些模糊的，受 Unix 启发的代码，是你识别 Node 中的错误类型的方式。
    if (error.code != "ENOENT") throw error;
    else return {status: 404, body: "File not found"};
  }
  if (stats.isDirectory()) {
    // 目录的话 就去读 这个目录底下的文件
    return {body: (await readdir(path)).join("\n")};
  } else {
    // 文件类型
    return {body: createReadStream(path),
            type: mime.getType(path)};
  }
};

const {rmdir, unlink} = require("fs").promises;

methods.DELETE = async function(request) {
  let path = urlPath(request.url);
  let stats;
  try {
    stats = await stat(path);
  } catch (error) {
    if (error.code != "ENOENT") throw error;
    else return {status: 204};
  }
  // 删除文件夹底下的文件
  if (stats.isDirectory()) await rmdir(path);
  else await unlink(path);
  // 删除链接 linux是这样的 没有引用之后会删除
  return {status: 204};
};

const {createWriteStream} = require("fs");

function pipeStream(from, to) {
  return new Promise((resolve, reject) => {
    from.on("error", reject);
    to.on("error", reject);
    // 管道的流里有错误 就报错了
    to.on("finish", resolve);
    from.pipe(to);
  });
}

methods.PUT = async function(request) {
  let path = urlPath(request.url);
  // 写一个文件
  await pipeStream(request, createWriteStream(path));
  return {status: 204};
};

const {mkdir} = require("fs").promises;

// curl -X MKCOL -d MKCOL http://localhost:8000/MKCOL
// 发起请求
methods.MKCOL = async function(request) {
  // make col 
  // 创建文件
  let path = urlPath(request.url);
  console.log("去创建 path");
  console.log(path);
  let stats;
  try {
    console.log("查看文件");
    stats = await stat(path);
    console.log("stats");
    console.log(stats);
  } catch (error) {
    if (error.code != "ENOENT") throw error;
    console.log("创建文件");
    await mkdir(path);
    return {status: 204};
  }
  // 他是目录了 就好了 说明创建成功
  if (stats.isDirectory()) return {status: 204};
  else return {status: 400, body: "Not a directory"};
};
